package com.ling.greedy.snake.game.ui.pages.game.mvi

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.ling.greedy.snake.game.model.Direction
import com.ling.greedy.snake.game.model.GameStatus
import com.ling.greedy.snake.game.model.Point
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

/**
 * @author Ling
 * @date :2025/7/3 16:29
 */
class GameViewModel : ViewModel() {

    val gameCol = 50
    private var gameRow = 50
    private val snackSpeed = 200L

    private val _gameState = MutableStateFlow(
        GameState(
            snake = listOf(Point(gameCol / 2 , gameRow / 2)),
            food = generateFood(listOf(Point(gameCol / 2 , gameRow / 2))),
            currentDirection = Direction.RIGHT,
        )
    )

    val gameState = _gameState.asStateFlow()

    private var moveSnackJob : Job? = null

    fun initGamePadSize(row : Int){
        gameRow = row
    }

    fun startGame(){
        _gameState.update { it.copy(gameState = GameStatus.PLAY) }
        moveSnack()
    }

    fun restart(){
        moveSnackJob?.cancel()
        _gameState.update {
            it.copy(
                gameState = GameStatus.PAUSE,
                snake = listOf(Point(gameCol / 2 , gameRow / 2)),
                score = 0,
                food = generateFood(listOf(Point(gameCol / 2 , gameRow / 2))),
                currentDirection = Direction.RIGHT,
            )
        }
    }

    fun pauseGame(){
        _gameState.update { it.copy(gameState = GameStatus.PAUSE) }
        moveSnackJob?.cancel()
    }

    fun changeDirection(newDirection: Direction) {
        _gameState.update { current ->
            if (canChangeDirection(current.currentDirection, newDirection)) {
                current.copy(currentDirection = newDirection)
            } else {
                current
            }
        }
    }

    private fun moveSnack(){
        moveSnackJob = viewModelScope.launch(Dispatchers.Default) {
            while (true){
                delay(snackSpeed)

                if(_gameState.value.gameState != GameStatus.PLAY) continue
                if(_gameState.value.gameState == GameStatus.FINISH) return@launch

                _gameState.let {
                    val head = it.value.snake.first()
                    val newHead = when(it.value.currentDirection){
                        Direction.UP -> head.copy(y = head.y - 1)
                        Direction.DOWN -> head.copy(y = head.y + 1)
                        Direction.LEFT -> head.copy(x = head.x - 1)
                        Direction.RIGHT -> head.copy(x = head.x + 1)
                    }

                    if(checkCollision(newHead)){
                        gameOver()
                        return@launch
                    }

                    val ateFood = newHead == it.value.food
                    val newSnake = if (ateFood) {
                        listOf(newHead) + it.value.snake
                    }else{
                        listOf(newHead) + it.value.snake.dropLast(1)  // 未吃到食物时,将尾部删除，添加新的头部，不是减少长度，而是移动
                    }

                    val newFood = if (ateFood) generateFood(newSnake) else it.value.food

                    it.update {
                        new-> new.copy(
                            snake = newSnake,
                            food = newFood,
                            score = if(ateFood) it.value.score + 1 else it.value.score
                        )
                    }
                }
            }
        }
    }

    private fun checkCollision(point : Point) : Boolean{
        if(point.x < 0 || point.x >= gameCol || point.y < 0 || point.y >= gameRow) return true
        return _gameState.value.snake.any { it == point }
    }

    private fun gameOver(){
        moveSnackJob?.cancel()
        _gameState.update { it.copy(gameState = GameStatus.FINISH) }
    }

    private fun canChangeDirection(currentDirection: Direction , newDirection: Direction) : Boolean{
        return when (currentDirection) {
            Direction.UP -> newDirection != Direction.DOWN
            Direction.DOWN -> newDirection != Direction.UP
            Direction.LEFT -> newDirection != Direction.RIGHT
            Direction.RIGHT -> newDirection != Direction.LEFT
        }
    }

    private fun generateFood(snake : List<Point>) : Point{
        val availablePositions = mutableListOf<Point>()
        for (x in 0 until gameCol) {
            for (y in 0 until gameRow) {
                val point = Point(x, y)
                if (!snake.contains(point)) {
                    availablePositions.add(point)
                }
            }
        }
        return availablePositions.random()
    }

}